package flex.messaging.security;

import java.lang.reflect.InvocationTargetException;
import java.security.Principal;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.acegisecurity.AbstractAuthenticationManager;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.AbstractAuthenticationToken;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import flex.messaging.FlexContext;
import flex.messaging.util.PropertyStringResourceLoader;

public class AcegiLoginCommand extends AppServerLoginCommand {

	public AcegiLoginCommand() {
	}

	public Principal doAuthentication(String username, Object credentials) throws SecurityException {
		String password = extractPassword(credentials);
		if (password != null) {
			HttpServletRequest request = FlexContext.getHttpRequest();
			Authentication auth = new UsernamePasswordAuthenticationToken(
					username, password);
			ApplicationContext ctx = WebApplicationContextUtils
					.getWebApplicationContext(request.getSession()
							.getServletContext());
			if (ctx != null) {
				AbstractAuthenticationManager authenticationManager = (AbstractAuthenticationManager) ctx
						.getBean("authenticationManager");
				SecurityContextHolder.getContext().setAuthentication(
						authenticationManager.authenticate(auth));
			}
			return (AbstractAuthenticationToken)SecurityContextHolder.getContext().getAuthentication();

		} else {
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	public boolean doAuthorization(Principal principal, List roles) throws SecurityException {
		boolean authorized = false;
		HttpServletRequest request = FlexContext.getHttpRequest();
		Authentication authentication = getAuthentication();

		if (request != null && principal != null
				&& principal.equals(authentication)
				&& isAuthenticated(authentication)) {
			authorized = verifyIfUserCanAccessService(roles, authentication);
		} else {
			SecurityException se = new SecurityException(
					new PropertyStringResourceLoader("flex.messaging.vendors"));
			se.setMessage("User not logged in!");
			throw se;
		}
		return authorized;
	}

	public boolean logout(Principal principal) throws SecurityException {
		HttpServletRequest request = FlexContext.getHttpRequest();
		Authentication authentication = getAuthentication(); 
		if (request != null) {
			// request.getSession().invalidate();
			 authentication.setAuthenticated(false); 
		}
		return true;
	}

	protected boolean isUserInRole(Authentication authentication, String role) {
		for (GrantedAuthority ga : authentication.getAuthorities()) {
			if (ga.getAuthority().matches(role))
				return true;
		}
		return false;
	}

	protected boolean isAuthenticated(Authentication authentication) {
		return authentication != null && authentication.isAuthenticated();
	}

	protected boolean verifyIfUserCanAccessService(List<String> roles,
			Authentication authentication) {

		boolean authorized = false;
		for (String role : roles) {
			if (isUserInRole(authentication, role)) {
				authorized = true;
				break;
			}
		}
		return authorized;
	}

	protected Authentication getAuthentication() {
		SecurityContext securityContext = SecurityContextHolder.getContext();
		Authentication authentication = securityContext.getAuthentication();
		return authentication;
	}

	protected void handleAuthorizationExceptions(InvocationTargetException e) {
		for (Throwable t = e; t != null; t = t.getCause()) {
			// Don't create a dependency to javax.ejb in SecurityService...
			if (t instanceof SecurityException
					|| t instanceof AccessDeniedException
					|| "javax.ejb.EJBAccessException".equals(t.getClass()
							.getName())) {
				SecurityException se = new SecurityException(
						new PropertyStringResourceLoader(
								"flex.messaging.vendors"));
				se.setMessage(t.getMessage());
				throw se;
			}
		}
	}
}
